package cbbx_sm.decision_maker;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import cbbx_sm.probabilistic_model.Cluster;
import cbbx_sm.probabilistic_model.Prediction;

public class SampleFromDistributionDecisionMaker implements IDecisionMaker{
	private double CUT_OFF;
	
	public SampleFromDistributionDecisionMaker(double cut_off){
		this.CUT_OFF = cut_off;
	}
	
	@Override
	public Decision makeDecision(Prediction currentPrediction) {
		List<CameraConfiguration> confs = new Vector<CameraConfiguration>();
		
		Cluster[] clusters = new Cluster[currentPrediction.getClusterProbabilities().keySet().size()];
		currentPrediction.getClusterProbabilities().keySet().toArray(clusters);
		
		List<Cluster> fusedClusters = new Vector<Cluster>();
		for (int i = 0; i < clusters.length; i++){
			Cluster cluster = clusters[i];
			fusedClusters.add(i, cluster);
		}
		
		HashMap<String, List<Cluster>> map = getCameraClusters(fusedClusters);
		
		for (String cameraId : map.keySet()){
			List<Cluster> curCameraClusters = map.get(cameraId);
			double[] probs = new double[curCameraClusters.size()];
			
			for (int i = 0; i < curCameraClusters.size(); i++){
				Cluster cluster = curCameraClusters.get(i);
				probs[i] = currentPrediction.getClusterProbability(cluster);
			}
			
			/*
			 * If all probability of clusters of a camera are 0.0,
			 * the decision is to stay-up.
			 */
			boolean flag = false;
			for (int i = 0; i < probs.length; i++){
				if (probs[i] != 0.0) {
					flag = true;
					break;
				}
			}
			if (!flag) continue;
			
			int[] plan = sampleKBasedOnDistribution(probs, 1);
			Cluster choosenCluster = curCameraClusters.get(plan[0]);
			double choosenProb = probs[plan[0]];
			
			if (choosenProb > CUT_OFF)
				confs.add(new CameraConfiguration(cameraId, choosenCluster));
		}
		
		Decision decision = new Decision(confs);
		
		return decision;
	}

	private HashMap<String, List<Cluster>> getCameraClusters(List<Cluster> fusedClusters){
		HashMap<String, List<Cluster>> map = new HashMap<String, List<Cluster>>();
		for (Cluster c : fusedClusters){
			if (!map.containsKey(c.getCameraId()))
				map.put(c.getCameraId(), new Vector<Cluster>());
			map.get(c.getCameraId()).add(c);
		}
		
		return map;
	}
	
	/***********************************************************************
	 * RONEN'S CODE
	 ***********************************************************************/
	private int[] sampleKBasedOnDistribution(double[] l_probs, int budget) {
		int[] newPlan = new int[budget];
		double totalProbs=0;
		double rndNumber;
		
		// create an array list as we are about to remove the entries as we select them
		ArrayList<Double> probsArray = new ArrayList<Double>();
		for (int i=0; i<l_probs.length;i++){
			probsArray.add(l_probs[i]);
			totalProbs+=l_probs[i];
		}
		//System.out.println("budget:"+budget);
		for (int i=0;i<budget; i++){
			//generate a random number between 0 and the total.
			rndNumber=Math.random()*totalProbs;
	
			// find the one that this number stands for.
			int foundIdx=findIndex(probsArray,rndNumber);			
			//  update the sum and set its entry to zero s.t. it won't get selected again.
			totalProbs=totalProbs-l_probs[foundIdx];
			probsArray.set(foundIdx,0.0);
			// select the camera for probing
			newPlan[i]=foundIdx;
			//System.out.println("index selected:"+newPlan[i]);
		}
		
		Arrays.sort(newPlan);
		return newPlan;
	}

	private int findIndex(ArrayList<Double> list, double d) {
		double sumOfElements=0;
		for (int i=0; i<list.size();i++){
			sumOfElements+=list.get(i);
			//System.out.println("totalSum"+sumOfElements+" d"+d);
			if (d<=sumOfElements)
				return i;
		}
		return -1;
	}
	/*************************************************************************/
	
	@Override
	public String toString(){
		return "" + this.getClass().getSimpleName() + "\t" + CUT_OFF;
	}
}
